Skip to content

Unsafe evolution: unsafe fields#83694

Merged
jjonescz merged 8 commits into
dotnet:mainfrom
jjonescz:Unsafe-35-Fields
May 22, 2026
Merged

Unsafe evolution: unsafe fields#83694
jjonescz merged 8 commits into
dotnet:mainfrom
jjonescz:Unsafe-35-Fields

Conversation

@jjonescz
Copy link
Copy Markdown
Member

@jjonescz jjonescz commented May 14, 2026

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the C# compiler’s “unsafe evolution” implementation so that fields can be explicitly marked unsafe under the updated memory safety rules, making field access require an unsafe context via RequiresUnsafeAttribute/CallerUnsafeMode.

Changes:

  • Add field support to RequiresUnsafeAttribute and update compiler tests to validate unsafe field behavior and metadata.
  • Implement CallerUnsafeMode for fields across source, metadata, and wrapper/synthesized field symbols (including synthesis and decoding of RequiresUnsafeAttribute).
  • Adjust binder behavior so unsafe on declarations does not introduce an unsafe region under updated rules, and emit diagnostics (not just DEBUG asserts) when inline-array element fields are caller-unsafe.

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs Updates the test definition of RequiresUnsafeAttribute to allow Field targets.
src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs Adds coverage for unsafe fields, including metadata validation for PE fields.
src/Compilers/CSharp/Portable/Symbols/Tuples/TupleFieldSymbol.cs Propagates CallerUnsafeMode from the underlying field for tuple element fields.
src/Compilers/CSharp/Portable/Symbols/Tuples/TupleErrorFieldSymbol.cs Explicitly sets CallerUnsafeMode to None for error tuple fields.
src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedLambdaCacheFieldSymbol.cs Sets synthesized lambda cache fields to CallerUnsafeMode.None.
src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedFieldSymbol.cs Sets synthesized fields to CallerUnsafeMode.None.
src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEnumValueFieldSymbol.cs Sets enum backing fields to CallerUnsafeMode.None.
src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs Sets auto-property backing fields to CallerUnsafeMode.None.
src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedPrimaryConstructorParameterBackingFieldSymbol.cs Sets primary-ctor backing fields to CallerUnsafeMode.None.
src/Compilers/CSharp/Portable/Symbols/SubstitutedFieldSymbol.cs Delegates CallerUnsafeMode to the underlying field for substituted fields.
src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs Implements explicit caller-unsafe fields under updated rules and synthesizes RequiresUnsafeAttribute.
src/Compilers/CSharp/Portable/Symbols/Source/SourceEnumConstantSymbol.cs Ensures enum constants remain CallerUnsafeMode.None.
src/Compilers/CSharp/Portable/Symbols/Source/FieldSymbolWithAttributesAndModifiers.cs Reports ERR_RequiresUnsafeAttributeInSource when RequiresUnsafeAttribute is applied to fields in source.
src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingFieldSymbol.cs Delegates CallerUnsafeMode to the underlying field for retargeting wrappers.
src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs Decodes/filter-caches RequiresUnsafeAttribute from metadata and exposes it via CallerUnsafeMode.
src/Compilers/CSharp/Portable/Symbols/FieldSymbol.cs Removes the previous “always None” default to allow field-specific CallerUnsafeMode.
src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.FieldSymbol.cs Sets anonymous type backing fields to CallerUnsafeMode.None.
src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineFieldSymbol.cs Sets state machine fields to CallerUnsafeMode.None.
src/Compilers/CSharp/Portable/Lowering/ClosureConversion/LambdaCapturedVariable.cs Sets captured-variable fields to CallerUnsafeMode.None.
src/Compilers/CSharp/Portable/Binder/Binder_Flags.cs Updates unsafe-region introduction rules under updated memory safety rules.
src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs Emits diagnostics for unsafe inline-array element field access.
src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs Emits diagnostics for unsafe inline-array conversions via caller-unsafe element fields.

Comment thread src/Compilers/CSharp/Portable/Binder/Binder_Flags.cs
Comment thread src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Comment thread src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs
@jjonescz jjonescz changed the title Unsafe evolution: allow unsafe fields Unsafe evolution: unsafe fields May 14, 2026
@jjonescz jjonescz marked this pull request as ready for review May 15, 2026 06:51
@jjonescz jjonescz requested a review from a team as a code owner May 15, 2026 06:51
@jjonescz jjonescz requested review from 333fred, AlekseyTs and Copilot May 15, 2026 06:51
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 24 out of 24 changed files in this pull request and generated 1 comment.

Comment thread src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs
@jjonescz
Copy link
Copy Markdown
Member Author

@333fred @AlekseyTs for reviews, thanks

@phil-allen-msft
Copy link
Copy Markdown
Member

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 pipeline(s), but failed to run 1 pipeline(s).

@jjonescz
Copy link
Copy Markdown
Member Author

@333fred @AlekseyTs for reviews, thanks

Comment thread src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs Outdated
Comment thread src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs
Copy link
Copy Markdown
Member

@333fred 333fred left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, assuming tests are passing

@jjonescz
Copy link
Copy Markdown
Member Author

@AlekseyTs for second review, thanks


diagnostics.ReportUseSite(elementField, syntax);
AssertNotUnsafeMemberAccess(elementField); // https://github.com/dotnet/roslyn/issues/82546: Support unsafe fields?
ReportDiagnosticsIfUnsafeMemberAccess(diagnostics, elementField, syntax);
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs May 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReportDiagnosticsIfUnsafeMemberAccess(diagnostics, elementField, syntax);

Do we actually access the field explicitly here? I don't think we do. Therefore, it feels strange that we check "safety" rules for the field. #Closed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that motivation for reporting use-site was mostly about reporting bad field types/declarations

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess, my question is whether it was an explicit design decision to check underlying field for "safety".

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess, my question is whether it was an explicit design decision to check underlying field for "safety".

The general rule we established, I think, is to check user-declared members that the compiler uses. But we don't need to check well-known members.

For example, it was discussed in last LDM for unsafe https://github.com/dotnet/csharplang/blob/main/meetings/2026/LDM-2026-05-13.md#implicit-calls-and-constructor-edge-cases:

When compiler-generated or implicit execution would call a caller-unsafe member, we will either require an unsafe context or produce an error; we will not silently allow the call.


Do we actually access the field explicitly here? I don't think we do.

That's a good point, I haven't realized that. I will remove this check and we can add an open question for inline array fields.

CheckFeatureAvailability(node, MessageID.IDS_FeatureInlineArrays, diagnostics);
diagnostics.ReportUseSite(elementField, node);
AssertNotUnsafeMemberAccess(elementField); // https://github.com/dotnet/roslyn/issues/82546: Support unsafe fields?
ReportDiagnosticsIfUnsafeMemberAccess(diagnostics, elementField, node);
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs May 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReportDiagnosticsIfUnsafeMemberAccess(diagnostics, elementField, node);

Similar question here #Closed

targetFramework: TargetFramework.Net100,
options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules())
.VerifyDiagnostics(
// (7,13): error CS9362: 'Buffer._element0' must be used in an unsafe context because it is marked as 'unsafe' or 'extern'
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs May 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// (7,13): error CS9362: 'Buffer._element0' must be used in an unsafe context because it is marked as 'unsafe' or 'extern'

Does generated code use the field explicitly? #Closed

@AlekseyTs
Copy link
Copy Markdown
Contributor

AlekseyTs commented May 20, 2026

Done with review pass (commit 5) #Closed

Copilot AI review requested due to automatic review settings May 21, 2026 10:27
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 24 out of 24 changed files in this pull request and generated 2 comments.

Comment thread src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs Outdated
Comment thread src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM (commit 8)

@jjonescz jjonescz merged commit 5823b9f into dotnet:main May 22, 2026
28 checks passed
@jjonescz jjonescz deleted the Unsafe-35-Fields branch May 22, 2026 08:19
@dotnet-policy-service dotnet-policy-service Bot added this to the Next milestone May 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants